package com.ivo.act.controller;

import com.ivo.act.common.result.PageResult;
import com.ivo.act.common.result.Result;
import com.ivo.act.common.utils.ResultUtil;
import com.ivo.act.common.utils.SpringUtil;
import org.activiti.bpmn.model.BpmnModel;
import org.activiti.bpmn.model.FlowNode;
import org.activiti.bpmn.model.SequenceFlow;
import org.activiti.engine.*;
import org.activiti.engine.history.HistoricActivityInstance;
import org.activiti.engine.repository.Deployment;
import org.activiti.engine.repository.ProcessDefinition;
import org.activiti.engine.runtime.Execution;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.Task;
import org.apache.commons.lang3.StringUtils;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import javax.sql.DataSource;
import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 流程实例管理
 * @author wj
 * @version 1.0
 */
@RestController
@RequestMapping("/processInstance")
public class ProcessInstanceController {

    @Resource
    private DataSource dataSource;

    @Resource
    private RuntimeService runtimeService;

    @Resource
    private TaskService taskService;

    @Resource
    private HistoryService historyService;

    @Resource
    private RepositoryService repositoryService;

    @RequestMapping("/start")
    public Result startProcess(String key) {
        Object object = SpringUtil.getBean("draftListener");
        System.out.println(object.getClass());
        runtimeService.startProcessInstanceByKey(key);
        return ResultUtil.success();
    }

    @GetMapping("/processInstances")
    public PageResult processInstances() {
        List<ProcessInstance> processInstancesList = runtimeService.createProcessInstanceQuery().processDefinitionId("DPY").list(); //runtimeService.createProcessInstanceQuery().list();
        List<Map> list = new ArrayList<>();
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
        for(ProcessInstance processInstance : processInstancesList) {
            Map map = new HashMap();
            map.put("id", processInstance.getProcessInstanceId());
            map.put("parent", "");
            map.put("def_id", processInstance.getProcessDefinitionId());
            map.put("def_key", "");
            map.put("def_name", "");
//            map.put("start_time", sdf.format(processInstance.getStartTime()));
//            map.put("start_user", processInstance.getStartUserId());
            map.put("activity_id", processInstance.getActivityId());
            list.add(map);
        }
        return ResultUtil.successPage(list);
    }

    @PostMapping("/complete")
    public Result complete(String processInstance_id, @RequestParam(required = false) Map variables) {
        List<Task> taskList = taskService.createTaskQuery().processInstanceId(processInstance_id).list();
        if(taskList.size()>0) {
            Task task = taskList.get(0);
            if(variables == null || variables.size()==0) {
                taskService.complete(task.getId());
            } else {
                variables.remove("processInstance_id");
                taskService.complete(task.getId(), variables);
            }
        }
        return ResultUtil.success();
    }

    @PostMapping("/history")
    public Result historicActivityInstance(String processInstanceId) {

//        String sql = "SELECT PROC_INST_ID_ FROM act_ru_task where id_ in ( " +
//                "select TASK from act_ex_execution where order_number='"+processInstanceId+"' " +
//                ")";
//
//        String processInstanceId_ = "";
//        try {
//            Connection connection = dataSource.getConnection();
//            Statement st = connection.createStatement();
//            ResultSet rs=st.executeQuery(sql);
//            while(rs.next()){
//                processInstanceId_ = rs.getString("PROC_INST_ID_");
//            }
//
//            rs.close();
//            st.close();
//            connection.close();
//        } catch (SQLException throwables) {
//            throwables.printStackTrace();
//        }finally {
//
//        }
//
//        if(StringUtils.isEmpty(processInstanceId_)) {
//            Map map_ = new HashMap();
//            map_.put("history", new ArrayList<>());
//            map_.put("highLightedActivitis", new ArrayList<>());
//            map_.put("highLightedFlows", new ArrayList<>());
//            map_.put("currIds", new ArrayList<>());
//            return ResultUtil.success(map_);
//        }


        ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processInstanceId(processInstanceId).list().get(0);
        if(processInstance == null) {
            return ResultUtil.success(new HashMap<>());
        }

        // 获取流程定义信息
        BpmnModel bpmnModel = repositoryService.getBpmnModel(processInstance.getProcessDefinitionId());

        //获取流程历史中已执行节点
        List<HistoricActivityInstance> historyList = historyService.createHistoricActivityInstanceQuery()
                .processInstanceId(processInstanceId)
                .orderByHistoricActivityInstanceId().asc().list();

        // 高亮环节id集合
        List<String> highLightedActivitis = new ArrayList<>();
        for (HistoricActivityInstance tempActivity : historyList) {
            String activityId = tempActivity.getActivityId();
            highLightedActivitis.add(activityId);
        }

        // 高亮线路id集合
        List<String> highLightedFlows = getHighLightedFlows(bpmnModel, historyList);

        Set<String> currIds = runtimeService.createExecutionQuery().processInstanceId(processInstanceId).list()
                .stream().map(e -> e.getActivityId()).collect(Collectors.toSet());
        Set<String> currIds_ = new HashSet<>();
        for(String currId : currIds) {
            if(StringUtils.isNotEmpty(currId)) currIds_.add(currId);
        }

        Map map = new HashMap();
        map.put("history", historyList);
        map.put("highLightedActivitis", highLightedActivitis);
        map.put("highLightedFlows", highLightedFlows);
        map.put("currIds", currIds_);
        return ResultUtil.success(map);
    }

    public List<String> getHighLightedFlows(BpmnModel bpmnModel, List<HistoricActivityInstance> historicActivityInstances) {
        // 24小时制
        SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        // 用以保存高亮的线flowId
        List<String> highFlows = new ArrayList<String>();

        for (int i = 0; i < historicActivityInstances.size() - 1; i++) {
            // 对历史流程节点进行遍历
            // 得到节点定义的详细信息
            FlowNode activityImpl = (FlowNode) bpmnModel.getMainProcess().getFlowElement(historicActivityInstances.get(i).getActivityId());
            // 用以保存后续开始时间相同的节点
            List<FlowNode> sameStartTimeNodes = new ArrayList<FlowNode>();
            FlowNode sameActivityImpl1 = null;
            // 第一个节点
            HistoricActivityInstance activityImpl_ = historicActivityInstances.get(i);
            HistoricActivityInstance activityImp2_;

            for (int k = i + 1; k <= historicActivityInstances.size() - 1; k++) {
                // 后续第1个节点
                activityImp2_ = historicActivityInstances.get(k);

                // 都是usertask，且主节点与后续节点的开始时间相同，说明不是真实的后继节点
                if (activityImpl_.getActivityType().equals("userTask") && activityImp2_.getActivityType().equals("userTask") &&
                        df.format(activityImpl_.getStartTime()).equals(df.format(activityImp2_.getStartTime()))) {

                } else {
                    // 找到紧跟在后面的一个节点
                    sameActivityImpl1 = (FlowNode) bpmnModel.getMainProcess().getFlowElement(historicActivityInstances.get(k).getActivityId());
                    break;
                }
            }
            // 将后面第一个节点放在时间相同节点的集合里
            sameStartTimeNodes.add(sameActivityImpl1);
            for (int j = i + 1; j < historicActivityInstances.size() - 1; j++) {
                // 后续第一个节点
                HistoricActivityInstance activityImpl1 = historicActivityInstances.get(j);
                // 后续第二个节点
                HistoricActivityInstance activityImpl2 = historicActivityInstances.get(j + 1);

                // 如果第一个节点和第二个节点开始时间相同保存
                if (df.format(activityImpl1.getStartTime()).equals(df.format(activityImpl2.getStartTime()))) {
                    FlowNode sameActivityImpl2 = (FlowNode) bpmnModel.getMainProcess().getFlowElement(activityImpl2.getActivityId());
                    sameStartTimeNodes.add(sameActivityImpl2);
                } else {
                    // 有不相同跳出循环
                    break;
                }
            }
            // 取出节点的所有出去的线
            List<SequenceFlow> pvmTransitions = activityImpl.getOutgoingFlows();

            // 对所有的线进行遍历
            for (SequenceFlow pvmTransition : pvmTransitions) {
                // 如果取出的线的目标节点存在时间相同的节点里，保存该线的id，进行高亮显示
                FlowNode pvmActivityImpl = (FlowNode) bpmnModel.getMainProcess().getFlowElement(pvmTransition.getTargetRef());
                if (sameStartTimeNodes.contains(pvmActivityImpl)) {
                    highFlows.add(pvmTransition.getId());
                }
            }
        }
        return highFlows;
    }

    @GetMapping("/diagram/{processInstanceId}")
    public void diagram(@PathVariable String processInstanceId, HttpServletResponse response) throws IOException {
        ProcessInstance processInstance = runtimeService.createProcessInstanceQuery()
                .processInstanceId(processInstanceId).singleResult();

        if(processInstance == null) {
            return;
        }

        ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery()
                .processDefinitionId(processInstance.getProcessDefinitionId()).singleResult();

        Deployment deployment = repositoryService.createDeploymentQuery()
                .deploymentId(processDefinition.getDeploymentId()).singleResult();

        InputStream in = repositoryService.getResourceAsStream(deployment.getId(), processDefinition.getResourceName());

        // 输出流程图
        ServletOutputStream outputStream = response.getOutputStream();
        byte[] b = new byte[2048];
        int len;
        while ((len = in.read(b, 0, b.length)) != -1) {
            outputStream.write(b, 0, len);
        }
    }

    @RequestMapping("/getVariables")
    public Result getVariables(String processInstanceId) {
        List<Execution> executionList = runtimeService.createExecutionQuery().processInstanceId(processInstanceId).list();
        Map map = new HashMap();
        for(Execution execution : executionList) {
            map.put(execution.getId(), runtimeService.getVariables(execution.getId()));
        }

        List<Task> taskList = taskService.createTaskQuery().processInstanceId(processInstanceId).list();
        for(Task task : taskList) {
            map.put(task.getId(), taskService.getVariables(task.getId()));
        }

        return ResultUtil.success(map);
    }

}
